package com.ziytek.web.citizen.service;

import com.google.gson.Gson;
import com.ziytek.web.citizen.config.ConfigInit;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.ServerConstant;
import com.ziytek.web.citizen.mapper.BasicCabinetIpMapper;
import com.ziytek.web.citizen.model.req.StatusQueryReq;
import com.ziytek.web.citizen.model.tcp.req.TcpStatusReq;
import com.ziytek.web.citizen.model.tcp.rsp.TcpStatusResponse;
import com.ziytek.web.citizen.model.vo.StatusVo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/***
 * 给web前端推送长连接数据
 * @author fenghx
 * */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CabinetStatusPushService {
    private final Gson gson;
    private final ConfigInit configInit;
    private final StatusQueryService statusQueryService;
    private final BasicCabinetIpMapper basicCabinetIpMapper;

    public void pushByIp(String ip, boolean online) {
        if (StringUtils.isBlank(ip)) {
            return;
        }

        var ips = basicCabinetIpMapper.queryCabinetIpInfoByIp(ip);
        if (null == ips || StringUtils.isBlank(ips.getCabinetNo())) {
            return;
        }

        var key = "statusVo_key_" + Integer.parseInt(ips.getCabinetNo());
        StatusVo statusVo = CaffeineUtil.get(key);
        if (null != statusVo) {
            statusVo.setStatus(online ? ServerConstant.STATUS_ONLINE : ServerConstant.STATUS_OFFLINE);
            log.info("[{}]设备上下线statusVo:{}", key, gson.toJson(statusVo));
            long expire = ServerConstant.STATUS_ONLINE.equals(statusVo.getStatus()) ? 20 * 60 : 24 * 60 * 60;
            CaffeineUtil.set(key, statusVo, expire);
        }

        // 更新机柜状态
        var pushData = (String) CaffeineUtil.get("status_cache_");
        if (StringUtils.isNotBlank(pushData)) {
            TcpStatusResponse rsp = gson.fromJson(pushData, TcpStatusResponse.class);
            List<StatusVo> data = rsp.getData();
            if (!CollectionUtils.isEmpty(data)) {
                for (StatusVo vo : data) {
                    if (StringUtils.isBlank(ips.getCabinetNo())) {
                        continue;
                    }
                    if (vo.getCabinetNo().equals(String.valueOf(Integer.parseInt(ips.getCabinetNo())))) {
                        vo.setStatus(online ? ServerConstant.STATUS_ONLINE : ServerConstant.STATUS_OFFLINE);
                        break;
                    }
                }

                CaffeineUtil.set("status_cache_", gson.toJson(rsp), 30);
            }
        }

        cabinetStatusPush();
    }


    public String cabinetStatusUpdate() {
        long start = System.currentTimeMillis();
        var rsp = statusQueryService.statusQuery(new StatusQueryReq());

        log.info("推送机柜状态查询,耗时:{}ms", System.currentTimeMillis() - start);

        if (rsp == null || CollectionUtils.isEmpty(rsp.getData())) {
            log.info("电池机柜状态长连接推送data为空");
            return null;
        }

        var response = new TcpStatusResponse();
        response.setTopic("cabinetStatus");
        response.setData(rsp.getData());

        if (!CollectionUtils.isEmpty(response.getData())) {
            response.getData().sort(Comparator.comparingInt(o -> Integer.parseInt(o.getCabinetNo())));
        }

        var pushData = gson.toJson(response);
        CaffeineUtil.set("status_cache_", pushData, 30);

        return pushData;
    }

    public void cabinetStatusPush() {
        //long start = System.currentTimeMillis();
        Map<String, Object> map = getStatusCache();
        if (CollectionUtils.isEmpty(map)) {
            log.info("电池机柜状态长连接缓存为空");
            return;
        }

        //log.info("推送机柜状态获取缓存,耗时:{}ms", System.currentTimeMillis() - start);

        var statusReq = (TcpStatusReq) map.get("req");
        var ctx = (ChannelHandlerContext) map.get("ctx");
        if (statusReq == null || ctx == null) {
            log.info("电池机柜状态长连接statusReq为空");
            return;
        }

        //start = System.currentTimeMillis();

        var pushData = (String) CaffeineUtil.get("status_cache_");
        //log.info("电池机柜状态长连接推送data->{}", pushData);
        if (StringUtils.isBlank(pushData)) {
            pushData = cabinetStatusUpdate();
        }

        if (StringUtils.isBlank(pushData)) {
            log.info("电池机柜状态长连接推送data为空,退出");
            return;
        }

        //log.info("推送机柜状态查询,耗时:{}ms", System.currentTimeMillis() - start);

        if (configInit.getConfig().isBasicDataPrinting()) {
            log.info("机柜状态数据推送:{}", pushData);
        }
        // tcp长连接推送数据
        // ctx.writeAndFlush(Unpooled.buffer().writeBytes(pushData.getBytes(StandardCharsets.UTF_8)));
        ctx.writeAndFlush(new TextWebSocketFrame(pushData));

        //log.info("推送机柜状态数据处理及推送,耗时:{}ms", System.currentTimeMillis() - start);
        //var clientIp = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
        //var port = ((InetSocketAddress) ctx.channel().remoteAddress()).getPort();
        //putReadMap(clientIp + "_" + port, ctx);
    }

    public Map<String, Object> getStatusCache() {
        var key = CaffeineUtil.CACHE_KEY_TOPIC_CAB_STATUS
                + CaffeineUtil.CACHE_KEY_SYMBOL;
        Map<String, CaffeineUtil.CacheObject<?>> map = CaffeineUtil.getAllStartWith(key);
        if (CollectionUtils.isEmpty(map)) {
            return Collections.emptyMap();
        }

        List<Map<String, Object>> result = map.values().stream()
                .filter(v -> v instanceof CaffeineUtil.CacheObject<?> cacheObject
                        && cacheObject.data instanceof Map m)
                .map(v -> (Map<String, Object>) v.data)
                .toList();

        return CollectionUtils.isEmpty(result) ? Collections.emptyMap() : result.getFirst();
    }

}
